package post

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	"mall/service/blog/model"
	"mall/service/blog/rpc/internal/svc"
	"mall/service/blog/rpc/internal/utils/errorhandler"
	"mall/service/blog/rpc/types/blog"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetPostByIdLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetPostByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPostByIdLogic {
	return &GetPostByIdLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetPostByIdLogic) GetPostById(in *blog.BaseIdReq) (*blog.PostInfo, error) {
	if (in.Filter == nil || len(*in.Filter) == 0) && (in.Id == nil || *in.Id == 0) {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	preloadMap, eagerLoadMap := modelx.ParsePreloadAndEagerLoad(in.Preload, in.EagerLoad)
	// logrus.Info(fmt.Sprintf("GetPostById preloadMap: %+v", preloadMap))
	//
	// logrus.Info(fmt.Sprintf("GetPostById eagerLoadMap: %+v", eagerLoadMap))
	//

	postEntityName := model.RpcEntityNames.BlogPost
	if _, ok := model.RpcEntityPreloadMap[postEntityName]; !ok {
		return &blog.PostInfo{}, nil
	}

	postRepositoryName := model.RepositoryNames.BlogPost
	columnMap, ok := model.RepositoryPreloadMap[postRepositoryName]
	if !ok {
		return &blog.PostInfo{}, nil
	}

	var entry *model.BlogPost
	if in.Filter != nil && len(*in.Filter) > 0 {
		var selectCols []string
		if preloadFields, ok := preloadMap[postEntityName]; ok {
			selectCols = modelx.ParseSelectColumns(preloadFields, columnMap)
		}
		selectCols = append(selectCols, model.BlogPostColumns.PostStatus)

		filterMap := modelx.ParseFilter(in.Filter, columnMap)
		logrus.Info(fmt.Sprintf("GetPostById filterMap: %+v", filterMap))
		if filterMap == nil {
			return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
		}

		if in.Id != nil && *in.Id > 0 {
			filterMap[model.BlogPostColumns.ID] = map[string][]any{
				"equalTo": {*in.Id},
			}
		}

		orderBy := modelx.ParseSorter(in.Sorter, columnMap)
		logrus.Info(fmt.Sprintf("GetPostById orderBy: %s", orderBy))
		if len(orderBy) == 0 {
			orderBy = model.BlogPostColumns.ID + " DESC"
		}

		res, _, err := l.svcCtx.Post.GetList(l.ctx, model.M{
			"pageSize": uint64(1),
			"orderBy":  orderBy,
			"select":   strings.Join(selectCols, ","),
		}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(res) == 0 {
			return &blog.PostInfo{}, nil
		}

		entry = res[0]
	} else {
		data, err := l.svcCtx.Post.FindOne(l.ctx, *in.Id)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		entry = data
	}

	if entry == nil {
		return &blog.PostInfo{}, nil
	}

	if entry.PostStatus == uint16(globalkey.PostStatusTrash) {
		return &blog.PostInfo{}, nil
	}

	eid := entry.ID
	subEntitySettingMap := make(map[string]map[string]*model.ColumnSetting)
	if eagerLoadEntities, ok := eagerLoadMap[postEntityName]; ok {
		for _, entity := range eagerLoadEntities {
			if entitySettingMap, ok := model.RpcEntityPreloadMap[entity]; ok {
				subEntitySettingMap[entity] = entitySettingMap
			}
		}
	}

	var content *string
	postDetailEntityName := model.RpcEntityNames.BlogPostDetail
	if _, ok := subEntitySettingMap[postDetailEntityName]; ok {
		subFilterMap := make(map[string]map[string][]any)
		subFilterMap[model.BlogPostDetailColumns.PostID] = map[string][]any{
			"equalTo": {eid},
		}
		list, _, err := l.svcCtx.Post.GetDetailList(l.ctx, model.M{
			"pageSize": uint64(1),
			"orderBy":  model.BlogPostDetailColumns.DetailID + " DESC",
			"select":   model.BlogPostDetailColumns.PostContent,
		}, -1, subFilterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(list) > 0 {
			content = pointy.GetPointer(list[0].PostContent)
		}
	}

	var media []*blog.TmpMediaInfo
	blogMediaEntityName := model.RpcEntityNames.BlogMedia
	if _, ok := subEntitySettingMap[blogMediaEntityName]; ok {
		blogMediaRepositoryName := model.RepositoryNames.BlogMedia
		subPreloadFields, hasSubPreloadFields := preloadMap[blogMediaEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[blogMediaRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)
			subSelectCols = append(subSelectCols, model.BlogAttachmentRelationshipColumns.ObjectID)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.BlogAttachmentRelationshipColumns.ObjectID] = map[string][]any{
				"equalTo": {eid},
			}
			subFilterMap[model.BlogAttachmentRelationshipColumns.ObjectType] = map[string][]any{
				"equalTo": {globalkey.AttachmentLogTypeKnowledgeBase},
			}
			orderBy := model.BlogAttachmentRelationshipColumns.AttachmentThumbnail + " DESC," +
				model.BlogAttachmentRelationshipColumns.AttachmentOrder
			list, _, err := l.svcCtx.Post.GetMedia(l.ctx, model.M{
				"orderBy": orderBy,
				"select":  strings.Join(subSelectCols, ","),
			}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				for _, v := range list {
					media = append(media, &blog.TmpMediaInfo{
						Uid:       &v.AttachmentRelationshipID,
						Id:        pointy.GetPointer(strconv.FormatInt(int64(v.AttachmentID), 10)),
						Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
						Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
						Uri:       &v.AttachmentSource,
						Url:       &v.AttachmentImageURL,
						AltText:   &v.AttachmentImageAlt,
						MetaData:  &v.AttachmentImageMeta,
					})
				}
			}
		}
	}

	var meta []*blog.MetaInfo
	var postMetaKeys []any
	postMetaKeyMap := make(map[string]string)
	postMetaEntityName := model.RpcEntityNames.BlogPostMeta
	if entitySettingMap, ok := subEntitySettingMap[postMetaEntityName]; ok {
		if subPreloadFields, ok := preloadMap[postMetaEntityName]; ok {
			for _, field := range subPreloadFields {
				if fieldSetting, ok := entitySettingMap[field]; ok && len(fieldSetting.Name) > 0 {
					postMetaKeys = append(postMetaKeys, fieldSetting.Name)
					postMetaKeyMap[fieldSetting.Name] = field
				}
			}

			if len(postMetaKeys) > 0 {
				subFilterMap := make(map[string]map[string][]any)
				subFilterMap[model.BlogPostMetumColumns.PostID] = map[string][]any{
					"equalTo": {eid},
				}
				subFilterMap[model.BlogPostMetumColumns.MetaKey] = map[string][]any{
					"in": postMetaKeys,
				}
				orderBy := model.BlogPostMetumColumns.MetaID + " DESC"
				list, _, err := l.svcCtx.Post.GetMeta(l.ctx, model.M{
					"orderBy": orderBy,
				}, -1, subFilterMap)
				if err != nil {
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}

				if len(list) > 0 {
					for _, v := range list {
						key := v.MetaKey
						if len(key) > 0 && v.MetaValue.Valid {
							if field, ok := postMetaKeyMap[key]; ok {
								meta = append(meta, &blog.MetaInfo{
									Id:    &v.MetaID,
									Key:   pointy.GetPointer(field),
									Value: pointy.GetPointer(v.MetaValue.String),
								})
							}
						}
					}
				}
			}
		}
	}

	var categories []*blog.TermInfo
	var tags []*blog.TermInfo
	blogTermEntityName := model.RpcEntityNames.BlogTerm
	if _, ok := subEntitySettingMap[blogTermEntityName]; ok {
		blogTermRepositoryName := model.RepositoryNames.BlogTerm
		subPreloadFields, hasSubPreloadFields := preloadMap[blogTermEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[blogTermRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			subSelectCols := modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			subFilterMap := make(map[string]map[string][]any)
			subFilterMap[model.BlogTermRelationshipColumns.ObjectID] = map[string][]any{
				"equalTo": {eid},
			}
			subFilterMap[model.BlogTermRelationshipColumns.ObjectType] = map[string][]any{
				"equalTo": {globalkey.PostTypeKnowledgeBase},
			}
			list, _, err := l.svcCtx.Term.GetRelationshipList(l.ctx, model.M{
				"select": model.BlogTermRelationshipColumns.TermID,
			}, -1, subFilterMap)
			if err != nil {
				return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}

			if len(list) > 0 {
				var tids []any
				for _, v := range list {
					tids = append(tids, v.TermID)
				}

				termFilterMap := make(map[string]map[string][]any)
				termFilterMap[model.BlogTermColumns.TermID] = map[string][]any{
					"in": tids,
				}
				subSelectCols = append(subSelectCols, model.BlogTermColumns.Taxonomy) // 必须获取以区分category及tag
				termList, _, err := l.svcCtx.Term.GetList(l.ctx, model.M{
					"select": strings.Join(subSelectCols, ","),
				}, -1, termFilterMap)
				if err != nil {
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}

				if len(termList) > 0 {
					for _, v := range termList {
						if v.Taxonomy == uint16(globalkey.TermTaxonomyCategory) {
							categories = append(categories, &blog.TermInfo{
								Id:   &v.TermID,
								Name: &v.Name,
								Slug: &v.Slug,
							})
						} else if v.Taxonomy == uint16(globalkey.TermTaxonomyTag) {
							tags = append(tags, &blog.TermInfo{
								Id:   &v.TermID,
								Name: &v.Name,
								Slug: &v.Slug,
							})
						}
					}
				}
			}
		}
	}

	if in.SearchLogId != nil {
		var visitorId uint64
		if in.VisitorId != nil {
			visitorId = *in.VisitorId
		}
		var platformId uint
		if in.PlatformId != nil {
			platformId = uint(*in.PlatformId)
		}
		l.svcCtx.User.InsertClickLog(l.ctx, nil, &model.BlogUserClickLog{
			UserID:      visitorId,
			PlatformID:  platformId,
			SearchLogID: *in.SearchLogId,
			ObjectID:    eid,
			ObjectType:  uint16(globalkey.PostTypePost),
			CreatedAt:   time.Now(),
		})
	}

	return &blog.PostInfo{
		Id:            pointy.GetPointer(eid),
		Title:         &entry.PostTitle,
		Slug:          &entry.PostName,
		Content:       content,
		Excerpt:       &entry.PostExcerpt,
		CommentStatus: pointy.GetPointer(uint32(entry.CommentStatus)),
		Uri:           &entry.GUID,
		ParentId:      &entry.PostParentID,
		Password:      &entry.PostPassword,
		CreatedAt:     pointy.GetPointer(modelx.FormatTime(entry.PostDate, "")),
		UpdatedAt:     pointy.GetPointer(modelx.FormatTime(entry.PostModifiedDate, "")),
		Sort:          pointy.GetPointer(uint32(entry.MenuOrder)),
		Meta:          meta,
		Media:         media,
		Categories:    categories,
		Tags:          tags,
	}, nil
}
